/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import lombok.AllArgsConstructor;
import javax.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseEntity;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.tenant.annotation.TenantIgnore;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.modules.admin.dto.RescueNoticeCreateDTO;
import org.springblade.modules.admin.dto.ai.RobotCreatePlanDTO;
import org.springblade.modules.admin.dto.rescue.*;
import org.springblade.modules.admin.dto.video.TicketFeedBackDTO;
import org.springblade.modules.admin.entity.*;
import org.springblade.modules.admin.enums.MaintainPlanContentEnum;
import org.springblade.modules.admin.enums.RescueNoticeConfirmTypeEnum;
import org.springblade.modules.admin.enums.RescueNoticeTypeEnum;
import org.springblade.modules.admin.enums.TaskStatusEnum;
import org.springblade.modules.admin.service.*;
import org.springblade.modules.admin.service.suger.IEstateTlwScreenService;
import org.springblade.modules.admin.vo.CurRescueNoticeVO;
import org.springblade.modules.admin.vo.RescueNoticeStatisticsVO;
import org.springblade.modules.admin.vo.ai.RobotCreatePlanVO;
import org.springblade.modules.admin.vo.rescue.ExistRescuePlanVO;
import org.springblade.modules.admin.vo.rescue.RescueNoticeDetailVO;
import org.springblade.modules.admin.vo.rescue.RescueNoticeListVO;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springblade.modules.admin.vo.RescueNoticeVO;
import org.springblade.modules.admin.wrapper.RescueNoticeWrapper;
import org.springblade.core.boot.ctrl.BladeController;
import java.util.*;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;

/**
 * 救援通知记录表 控制器
 *
 * @author ysq
 * @since 2023-09-14
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/admin/rescue/notice")
@Api(value = "救援通知记录表", tags = "救援通知记录表接口")
public class RescueNoticeController extends BladeController {

	private final IRescueNoticeService rescueNoticeService;
	private final IEstateTlwScreenService tlwScreenService;
	private final IMaintainPlanContentService maintainPlanContentService;
	private final IChatGroupBuildingService chatGroupBuildingService;
	private final IElevatorDetailService elevatorDetailService;

	/**
	 * 详情
	 */
	@GetMapping("/detail")
	@ApiOperationSupport(order = 1)
	@ApiOperation(value = "详情", notes = "传入rescueNotice")
	public R<RescueNoticeVO> detail(RescueNotice rescueNotice) {
		RescueNotice detail = rescueNoticeService.getOne(Condition.getQueryWrapper(rescueNotice));
		return R.data(RescueNoticeWrapper.build().entityVO(detail));
	}

	/**
	 * 自定义分页 救援通知记录表
	 */
	@GetMapping("/page")
	@ApiOperationSupport(order = 2)
	@ApiOperation(value = "分页")
	public R<IPage<RescueNoticeVO>> page(RescueNoticeVO rescueNotice, Query query) {
		IPage<RescueNoticeVO> pages = rescueNoticeService.selectRescueNoticePage(Condition.getPage(query), rescueNotice);
		return R.data(pages);
	}

	@GetMapping("/statistics")
	@ApiOperationSupport(order = 3)
	@ApiOperation(value = "统计")
	public R<RescueNoticeStatisticsVO> statistics() {
		return R.data(rescueNoticeService.statistics());
	}

	/**
	 * 修改 救援通知记录表
	 */
	@PostMapping("/update")
	@ApiOperationSupport(order = 4)
	@ApiOperation(value = "修改")
	public R update(@Valid @RequestBody RescueNotice rescueNotice) {
		return R.status(rescueNoticeService.updateById(rescueNotice));
	}

	@GetMapping("/cur/list")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "紧急呼救列表")
	public R<List<CurRescueNoticeVO>> curRescueNoticeList() {
		return R.data(rescueNoticeService.curRescueNoticeList(AuthUtil.getTenantId()));
	}

	/**
	 * SSE实时推送紧急呼救列表
	 * 使用Server-Sent Events技术，提供实时数据推送
	 */
	@GetMapping(value = "/cur/list/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "SSE实时推送紧急呼救列表")
	public SseEmitter curRescueNoticeSSE() {
		// 获取当前租户ID
		String tenantId = AuthUtil.getTenantId();

		SseEmitter emitter = new SseEmitter(0L); // 0表示无超时

		// 设置连接建立时的回调
		emitter.onCompletion(() -> {
			log.info("SSE连接完成");
			// 清理资源
			cleanupEmitter(emitter);
		});

		emitter.onTimeout(() -> {
			log.info("SSE连接超时");
			// 清理资源
			cleanupEmitter(emitter);
		});

		emitter.onError((ex) -> {
			log.error("SSE连接异常", ex);
			// 清理资源
			cleanupEmitter(emitter);
		});

		// 异步发送初始数据
		CompletableFuture.runAsync(() -> {
			try {
				// 发送初始数据
				List<CurRescueNoticeVO> initialData = rescueNoticeService.curRescueNoticeList(tenantId);
				emitter.send(SseEmitter.event()
					.name("initial")
					.data(initialData));

				// 启动智能推送任务
				startSmartPush(emitter, tenantId);

			} catch (IllegalStateException e) {
				log.info("SSE连接已关闭，无法发送初始数据");
			} catch (Exception e) {
				log.error("SSE发送初始数据失败", e);
				try {
					emitter.send(SseEmitter.event()
						.name("error")
						.data("数据获取失败: " + e.getMessage()));
				} catch (IllegalStateException ex) {
					log.info("SSE连接已关闭，无法发送错误信息");
				} catch (IOException ex) {
					log.error("发送错误信息失败", ex);
				}
			}
		});

		return emitter;
	}

	/**
	 * 启动智能推送任务
	 * 基于数据变化检测，减少不必要的推送
	 */
	private void startSmartPush(SseEmitter emitter, String tenantId) {
		CompletableFuture.runAsync(() -> {
			List<CurRescueNoticeVO> lastData = null;
			int noChangeCount = 0;

			while (true) {
				try {
					// 检查连接是否已关闭
					if (emitter == null) {
						log.info("SSE连接已关闭，停止推送");
						break;
					}

					// 获取当前数据，使用租户ID
					List<CurRescueNoticeVO> currentData = rescueNoticeService.curRescueNoticeList(tenantId);

					// 检测数据是否发生变化
					boolean hasChanged = hasDataChanged(lastData, currentData);

					if (hasChanged) {
						// 数据有变化，立即推送
						emitter.send(SseEmitter.event()
							.name("update")
							.data(currentData));

						lastData = ObjectUtil.cloneByStream(currentData);
						noChangeCount = 0;
					} else {
						noChangeCount++;

						// 如果长时间没有变化，适当降低推送频率
						long sleepTime = noChangeCount > 10 ? 10000 : 5000; // 当数据连续10次未发生变化时，将推送间隔从5秒延长至10秒
						Thread.sleep(sleepTime);

						// 定期发送心跳保持连接
						if (noChangeCount % 6 == 0) { // 每30秒发送一次心跳
							emitter.send(SseEmitter.event()
								.name("heartbeat")
								.data("ping"));
						}
					}

				} catch (InterruptedException e) {
					Thread.currentThread().interrupt();
					log.info("推送任务被中断");
					break;
				} catch (IllegalStateException e) {
					// SSE连接已关闭
					log.info("SSE连接已关闭，停止推送");
					break;
				} catch (Exception e) {
					log.error("智能推送数据失败", e);
					try {
						emitter.send(SseEmitter.event()
							.name("error")
							.data("推送失败: " + e.getMessage()));
					} catch (IllegalStateException ex) {
						log.info("SSE连接已关闭，停止推送");
						break;
					} catch (IOException ex) {
						log.error("发送错误信息失败", ex);
						break;
					}
				}
			}
		});
	}

	/**
	 * 检测数据是否发生变化
	 */
	private boolean hasDataChanged(List<CurRescueNoticeVO> oldData, List<CurRescueNoticeVO> newData) {
		if (oldData == null || newData == null) {
			return true;
		}

		if (oldData.size() != newData.size()) {
			return true;
		}

		// 简单的数据变化检测，可以根据实际需求优化
		for (int i = 0; i < newData.size(); i++) {
			CurRescueNoticeVO oldItem = oldData.get(i);
			CurRescueNoticeVO newItem = newData.get(i);

			if (!Objects.equals(oldItem.getPlanId(), newItem.getPlanId()) ||
				!Objects.equals(oldItem.getPlanStatus(), newItem.getPlanStatus())) {
				return true;
			}
		}

		return false;
	}

	/**
	 * 清理SSE资源
	 */
	private void cleanupEmitter(SseEmitter emitter) {
		try {
			if (emitter != null) {
				emitter.complete();
			}
		} catch (Exception e) {
			log.error("清理SSE资源失败", e);
		}
	}

	/**
	 * 测试专用：手动创建救援预警接口
	 */
	@PostMapping("/test/create")
	@ApiOperationSupport(order = 6)
	@ApiOperation(value = "测试专用：手动创建救援预警接口")
	public R<Long> testCreate(@RequestBody RescueNoticeCreateDTO noticeCreate) {
		Long noticeId = null;
		try {
			noticeId = rescueNoticeService.rescueNoticeSave(noticeCreate);
		} catch (Exception e) {
			log.warn("紧急呼叫-创建救援预警:[{}],异常内容:[{}]", DateUtil.now(), e.getMessage());
		}
		return R.data(noticeId);
	}

	/**
	 * 2025-07 告警分页列表
	 * 包含：梯联网（物联网AI告警）、AI告警信息列表
	 */
	@PostMapping("/search/list")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "救援告警列表")
	public R<IPage<RescueNoticeListVO>> getNoticeListByCondition(@RequestBody RescueNoticeListDTO rescueNoticeListDTO,
																 @RequestBody Query query){
		return R.data(rescueNoticeService.getNoticeListByCondition(Condition.getPage(query), rescueNoticeListDTO));
	}

	/**
	 * 根据告警id 获取救援告警详情
	 * @param rescueNoticeId 告警id
	 * @return RescueNoticeDetailVO 告警结果
	 */
	@GetMapping("/detail/by/id")
	@ApiOperationSupport(order = 8)
	@ApiOperation(value = "救援告警详情")
	public R<RescueNoticeDetailVO> getNoticeDetail(Long rescueNoticeId){
		return R.data(rescueNoticeService.getNoticeDetailById(rescueNoticeId));
	}

	/**
	 * 确认救援告警
	 */
	@PostMapping("/confirm")
	@ApiOperationSupport(order = 9)
	@ApiOperation(value = "确认救援告警")
	public R<Long> submit(@RequestBody RescueNoticeConfirmDTO confirmDTO){
 	    // 0.参数校验
		if(Func.isEmpty(confirmDTO) || Func.isEmpty(confirmDTO.getId())){
			throw new ServiceException("需要传告警id");
		}
		Long currentNoticeId = confirmDTO.getId();
		// 1.调用接口，获取救援告警
		RescueNotice currentNotice = rescueNoticeService.getById(currentNoticeId);
		if(Func.isEmpty(currentNotice)){
			throw new ServiceException("当前告警记录不存在");
		}
		// 2025-08 新增 已确认的情况抛异常
		if(!currentNotice.getConfirmType().equals(RescueNoticeConfirmTypeEnum.NOT_CONFIRM.getConfirmType())){
			throw new ServiceException("报警已经确认，无需再确认");
		}
		// 2. 赋值并提交
		currentNotice.setRemark(confirmDTO.getRemark());
		currentNotice.setConfirmType(confirmDTO.getConfirmType());
		currentNotice.setConfirmTime(new Date());
		currentNotice.setConfirmUserId(AuthUtil.getUserId());
		currentNotice.setConfirmUser(AuthUtil.getNickName());
		if(Func.isEmpty(confirmDTO.getConfirmType()) || confirmDTO.getConfirmType().equals(RescueNoticeConfirmTypeEnum.MISREPORT.getConfirmType())){
			confirmDTO.setOperation(3);
		}
		// 3. 状态判断、部分不往下执行的直接组装回调信息
		TicketFeedBackDTO feedBack = new TicketFeedBackDTO();
		feedBack.setDeviceId(currentNotice.getDeviceId());
		feedBack.setRescueId(currentNotice.getCallId());
		Long planId = null;
		if(Func.isNotEmpty(confirmDTO.getConfirmType())){
			if(confirmDTO.getConfirmType().equals(RescueNoticeConfirmTypeEnum.MISREPORT.getConfirmType())){
				// 误报的情况发送误报的回调信息
				rescueNoticeService.updateById(currentNotice);
				// 给梯联网发误报回调
				feedBack.setIsCreateRescue(false);
				// 给梯联网进行回调
				feedBack.setState(2);
				tlwScreenService.ticketFeedback(feedBack);
				return R.data(null);
			}
		}
		if(Integer.valueOf(3).equals(confirmDTO.getOperation())
			|| Integer.valueOf(2).equals(confirmDTO.getOperation())){
			rescueNoticeService.updateById(currentNotice);
			if(Integer.valueOf(RescueNoticeConfirmTypeEnum.CANNOT_CONFIRM.getConfirmType()).equals(confirmDTO.getConfirmType())){
				// 给梯联网发确认但不创建工单的回调
				feedBack.setIsCreateRescue(false);
				// 给梯联网进行回调
				feedBack.setState(4);
				tlwScreenService.ticketFeedback(feedBack);
			}
			// 误报的情况发送误报的回调信息
			feedBack.setIsCreateRescue(false);
			// 给梯联网进行回调
			feedBack.setState(2);
			tlwScreenService.ticketFeedback(feedBack);
			return R.data(null);
		}

		// 重复创建校验 有在途的工单，则不创建工单
		if (currentNotice.getElevatorId() > 0) {
			List<Integer> statusList = Arrays.asList(TaskStatusEnum.JY_DISPATCH.getStatus(), TaskStatusEnum.JY_TO_START.getStatus(), TaskStatusEnum.JY_PROCESSED.getStatus(), TaskStatusEnum.JY_ARRIVE.getStatus());
			int rescueOrderCount = maintainPlanContentService.count(Wrappers.<MaintainPlanContent>lambdaQuery()
				.eq(MaintainPlanContent::getElevatorId, currentNotice.getElevatorId())
				.eq(MaintainPlanContent::getTypeStatus, MaintainPlanContentEnum.MAINTAIN_KR.getId())
				.in(MaintainPlanContent::getStatus, statusList));
			if (rescueOrderCount > 0) {
				rescueNoticeService.updateById(currentNotice);
				log.warn("当前告警[{}]有在途工单，不创建工单", currentNotice.getId());
				return R.data(null);
			}
		}

		// 3. 创建救援工单
		// 创建救援工单，当非误报，且为 创建救援工单操作
		String eventCode = currentNotice.getEventCode();
		if(Func.isEmpty(eventCode)){
			currentNotice.setEventCode(RescueNoticeTypeEnum.ONE_TOUCH.getEventCode());
			log.warn("当前告警来源为空");
		}

		if(currentNotice.getEventCode().equals(RescueNoticeTypeEnum.ONE_TOUCH.getEventCode())){
			TlwMalfunctionRescueDTO tlwMalfunctionRescue = new TlwMalfunctionRescueDTO();
			tlwMalfunctionRescue.setDeviceId(currentNotice.getDeviceId());
			tlwMalfunctionRescue.setFloor(currentNotice.getFloorNumber());
			tlwMalfunctionRescue.setFrom(1);
			tlwMalfunctionRescue.setRescueId(currentNotice.getCallId());
			tlwMalfunctionRescue.setName("梯联网");
			planId = maintainPlanContentService.addTlwRescuePlan(tlwMalfunctionRescue);
			currentNotice.setPlanId(planId);
			// 给梯联网发送创建救援工单回调
			if(confirmDTO.getConfirmType().equals(RescueNoticeConfirmTypeEnum.CONFIRM_ACCURATE.getConfirmType())){
				feedBack.setState(3);
			}else{
				feedBack.setState(4);
			}
			feedBack.setIsCreateRescue(true);
			tlwScreenService.ticketFeedback(feedBack);
		}
		else{
			// 直接创建工单
			RobotCreatePlanDTO robotCreatePlanDTO = new RobotCreatePlanDTO();
			robotCreatePlanDTO.setType(MaintainPlanContentEnum.MAINTAIN_KR.getId());
			robotCreatePlanDTO.setEventCode(currentNotice.getEventCode());
			// 根据项目id查询roomId
			RobotCreatePlanVO robotCreatePlanVO = new RobotCreatePlanVO();
			if(Func.isNotEmpty(currentNotice.getElevatorId())){
				robotCreatePlanVO.setElevatorId(currentNotice.getElevatorId());
				List<ElevatorDetail> elevatorAddressList = elevatorDetailService.list(new LambdaQueryWrapper<ElevatorDetail>()
					.eq(ElevatorDetail::getElevatorId, currentNotice.getElevatorId())
					.eq(BaseEntity::getIsDeleted, 0)
				);
				if(elevatorAddressList.size() == 1){
					robotCreatePlanDTO.setElevatorAddress(elevatorAddressList.get(0).getElevatorAddress());
				}
			}else{
				robotCreatePlanVO.setElevatorId(0L);
			}
			robotCreatePlanVO.setBuildingId(currentNotice.getBuildingId());
			robotCreatePlanVO.setGroupTenantId(currentNotice.getTenantId());
			planId = chatGroupBuildingService.createKrPlanFromRobot(robotCreatePlanVO, robotCreatePlanDTO);
			currentNotice.setPlanId(planId);
			// 给梯联网发送创建救援工单回调
			if(confirmDTO.getConfirmType().equals(RescueNoticeConfirmTypeEnum.CONFIRM_ACCURATE.getConfirmType())){
				feedBack.setState(3);
			}else{
				feedBack.setState(4);
			}
			feedBack.setIsCreateRescue(true);
			if(!RescueNoticeTypeEnum.CHAT_GROUP_AI.getEventCode().equals(currentNotice.getEventCode())){
				tlwScreenService.ticketFeedback(feedBack);
			}
		}
		rescueNoticeService.updateById(currentNotice);
		return R.data(planId);
	}

	/**
	 * 抬头统计
	 */
	@GetMapping ("/title/statistics")
	@ApiOperation("分页列表抬头统计")
	@ApiOperationSupport(order = 10)
	public R<Map<String,Integer>> titleStatistics(){
		Map<String, Integer> titleStatistics = new HashMap<>(2);
		int total = rescueNoticeService.count(new LambdaQueryWrapper<RescueNotice>()
			.eq(BaseEntity::getIsDeleted, 0)
		);
		int unConfirm = rescueNoticeService.count(new LambdaQueryWrapper<RescueNotice>()
			.eq(BaseEntity::getIsDeleted, 0)
			.eq(RescueNotice::getConfirmType,-1)
		);
		int confirmed = total - unConfirm;
		titleStatistics.put("total", total);
		titleStatistics.put("unConfirm",unConfirm);
		titleStatistics.put("confirmed", confirmed);
		return R.data(titleStatistics);
	}

	/**
	 * 微信群创建告警信息
	 */
	@TenantIgnore
	@PostMapping("/ai/create")
	@ApiOperation("微信群创建告警信息")
	@ApiOperationSupport(order = 11)
	public R<Long> createRescueNoticeFromGroup(@RequestBody @Valid GroupAINoticeCreateDTO groupAINoticeCreateDTO){
		return R.data(rescueNoticeService.createRescueNoticeFromGroup(groupAINoticeCreateDTO));
	}

	/**
	 * 进行中的救援工单
	 */
	@GetMapping("/exist/list")
	@ApiOperation("进行中的救援工单")
	@ApiOperationSupport(order = 12)
	public R<List<ExistRescuePlanVO>> existRescueNoticeList(Long elevatorId){
		return R.data(rescueNoticeService.existRescueNoticeList(elevatorId));
	}

	@PostMapping("/feedback")
	@ApiOperationSupport(order = 13)
	@ApiOperation(value = "给梯联网发送回调")
	public R<Boolean> feedback(@RequestBody TicketFeedBackDTO feedBack){
		if(Func.isEmpty(feedBack)){
			throw new ServiceException("梯联网回调参数不能为空");
		}
		return R.data(tlwScreenService.ticketFeedback(feedBack));
	}
}
